import requests
import allure

from utils.content_type import ContentType
from utils.global_data import global_data
from utils.response_options import ValidatableResponse, ExtractableResponse
from utils.logger import log
from utils.read_yaml import yaml_config
from utils.function_helper import FunctionHelper


class BaseApi(object):

    path = None
    method = None
    headers = None
    params = None
    json = None
    data = None
    files = None
    timeout = 30
    verify = False
    base_uri=None
    port=None

    def __init__(self):

        self.application_config = yaml_config.read_application_config("application.yaml")
        if self.base_uri is None:
            self.base_uri = self.application_config['host']
        if self.port is None:
            self.port = self.application_config['port']
        self.url = self.base_uri + ":" + str(self.port) + self.path

    def run(self, res=None):

        if res is None:
            self.__covert_data()
        else:
            self.__covert_data(res.VStep)

        with requests.session() as r:

            log.info("Send Request... url:{},method:{},params:{},headers:{},data:{},json:{}" \
                                .format(self.url, self.method, self.params, self.headers, self.data, self.json))

            try:

                self.__response = r.request(url=self.url,method=self.method,
                                                 params=self.params,headers=self.headers,
                                                 data=self.data,json=self.json,files=self.files,
                                                 timeout=self.timeout,verify=self.verify)
                self.__print_report()
                log.info("Get Response Data... url:{},,headers:{},response data:{}"
                                    .format(self.url,self.__response.headers,self.__response.text.replace('\r','').replace('\n','')))
                return self
            except Exception as e:
                log.error("An exception occurred while requesting :{}".format(e))

    def assert_that(self):
        return ValidatableResponse(self.__response)

    def __covert_data(self, res=None):
        global_headers = self.application_config['headers']
        if self.headers is None or len(self.headers) == 0:
            self.headers = global_headers
        else:
            for key, value in global_headers.items():
                if key not in self.headers:
                    self.headers[key] = value

        self.url = global_data.resolve_global_var(self.url, res)
        self.__replace_global_data(self.headers, res)
        self.__replace_global_data(self.params, res)
        self.__replace_global_data(self.json, res)
        self.__replace_global_data(self.data, res)

    def __replace_global_data(self, src_data, res):

        if src_data is not None:
            for key in src_data.keys():
                value = src_data[key]
                if isinstance(value, str):
                    src_data[key] = FunctionHelper.parse_function_str(global_data.resolve_global_var(value, res))

    def url(self, url):
        self.url = url
        return self

    def method(self, method):
        self.method = method
        return self

    def content_type(self, content_type: ContentType):
        self.headers['Content-Type'] = content_type
        return self

    def with_params(self, params):
        self.params = params
        return self

    def with_headers(self, headers):
        self.headers.update(headers)
        return self

    def with_cookies(self, cookies):
        self.cookies = cookies
        return self

    def with_data(self, data):
        self.data = data
        return self

    def with_json(self, json):
        self.json = json
        return self

    def with_path_params(self, params):
        for key, value in params.items():
            placeholder = "{" + key + "}"
            self.url = self.url.replace(placeholder, str(value))
            print(self.url)
        return self

    def verify(self, verify):
        self.verify = verify
        return self

    @allure.step('Send Request')
    def __print_report(self):

        allure.attach(name="Request url", body=self.url)
        allure.attach(name="Request method", body=str(self.method))
        allure.attach(name="Request headers", body=str(self.headers))

        if self.params is not None:
            allure.attach(name="Request param", body=str(self.params))

        if self.data is not None:
            allure.attach(name="Request data", body=str(self.data))

        if self.json is not None:
            allure.attach(name="Request json", body=str(self.json))

        if self.__response is not None:
            allure.attach(name="Response data", body=self.__response.text)


class TestStep(object):
    def __init__(self, run):
        self.__run = run

    def response(self) -> requests.Response:
        return self.__run.response()

    @property
    def VStep(self):
        if type(self.__run) == ExtractableResponse:
            return self.__run.step_values()